package me.ree.zkproduct.manager.impl;

import lombok.extern.slf4j.Slf4j;
import me.ree.zkproduct.annotation.MethodCallCount;
import me.ree.zkproduct.constants.DefaultValueConstants;
import me.ree.zkproduct.entity.Orders;
import me.ree.zkproduct.entity.Product;
import me.ree.zkproduct.manager.IProductOrdersManager;
import me.ree.zkproduct.service.IOrdersService;
import me.ree.zkproduct.service.IProductService;
import me.ree.zkproduct.util.JsonUtil;
import me.ree.zkproduct.util.Resp;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Component
public class ProductOrdersManagerImpl implements IProductOrdersManager {

    @Autowired
    private IProductService productServiceImpl;

    @Autowired
    private IOrdersService ordersServiceImpl;

    @Autowired
    private CuratorFramework curatorFramework;


    @MethodCallCount
    @Transactional
    public Resp<String> reduceStock(Long productId) {
        log.info("reduceStock begin...");
        if (productId == null) {
            return Resp.fail("商品ID不能为空");
        }

        // 同一种商品使用同一个锁
        String lockName = "/product-" + productId;
        InterProcessMutex lock = new InterProcessMutex(curatorFramework, lockName);

        try {
            lock.acquire();

            Product product = productServiceImpl.getById(productId);
            if (product != null && product.getStock() <= 0) {
                log.info("reduce row <= 0， product：{}", JsonUtil.toJson(product));
                return Resp.fail("该商品已经被抢光，stock=" + product.getStock());
            }

            long currentTime = System.currentTimeMillis();
            // 扣减商品
            product.setRemark("old version="+product.getVersion());
            product.setGmtModified(currentTime);
            int row = productServiceImpl.reduceStock(product);
            if (row < 1) {
                log.info("reduce row < 1，product：{}", JsonUtil.toJson(product));
                return Resp.fail("当前排队人数过多，请稍后重试，row=" + row);
            }

            if (row > 1) { // 此处是防止有特殊情况导致每次扣减的商品超过1件，除非业务逻辑出现bug，否则一般不会出现
                log.info("reduce row > 1，product：{}", JsonUtil.toJson(product));
                // 抛出异常，并触发回滚
                throw new RuntimeException("每次扣减商品的数量不能超过1件");
            }

            // 插入订单信息
            Orders orders = new Orders();
            orders.setProductCode(product.getProductCode());
            orders.setUserCode(DefaultValueConstants.TEST_USER_CODE_PRE + currentTime);
            orders.setGmtCreate(currentTime);
            orders.setGmtModified(currentTime);
            ordersServiceImpl.save(orders);
            log.info("reduce successful， product：{}", JsonUtil.toJson(product));
        } catch (Exception e) {
            log.error("申请分布式锁出错", e);
            // 申请锁出错时，还未扣减商品，所以不需要回滚，只需要返回错误信息给调用方即可
            return Resp.fail("申请分布式锁出错");
        } finally {
            // 释放锁时存在两种情况：都不需要触发事务的回滚
            // 1.如果商品已经扣减成功：应该继续执行后续逻辑
            // 2.如果是在申请锁的时候出错，商品尚未扣减，此时catch的return语句会返回错误信息给调用方
            try {
                lock.release();
            } catch (Exception e) {
                log.error("释放分布式锁出错", e);
            }
        }

        return Resp.ok("抢购成功");
    }
}
